﻿//###HEADER###
//Title: RevitPlus
//Description of Program: Utilzing Revit geometry and properties, user is able to perform an EnergyPlus calculation within Revit.
//Class:GetZones
//Description of Class:  Obtains all room/zone data from Revit. This data includes walls, roof, and floor associated with each zone.
//                       This class also creates lists of points associated with each wall, roof , and floor. GetZones Class also contains a catch incase Room has area of 0. 
//       
//By: Steve Chick & Alexander Bernstein
//Date: 12/18/2012
//Version: 001

using System;
using System.IO;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Forms;

using Autodesk.Revit.DB;
using Autodesk.Revit.DB.Architecture;
using Autodesk.Revit.UI;
using Autodesk.Revit.UI.Selection;
using Autodesk.Revit.ApplicationServices;
using Autodesk.Revit.Attributes;

namespace Revit_Addin1 
{
    [TransactionAttribute(TransactionMode.Manual)]
    [RegenerationAttribute(RegenerationOption.Manual)]

    //Define Class
    public class GetZone
    {
        
        public void getzones(ExternalCommandData commandData)
        {
            UIApplication uiApp = commandData.Application;
            Document doc = uiApp.ActiveUIDocument.Document;
            UIDocument uidoc = new UIDocument(doc);
            //Revit gives values in {ft} and energy plus reads values as {m}
            //The following conversion converts the unit of length
            Double MetricConversion = 0.3048;

            //Filter for all rooms in project
            FilteredElementCollector collector = new FilteredElementCollector(doc);
            RoomFilter roomfilter = new RoomFilter();

            //Create a ID iterator 
            collector.WherePasses(roomfilter);
            FilteredElementIdIterator roomIDitr = collector.GetElementIdIterator();
            roomIDitr.Reset();

            //Clear all previous values in the list
            StoredVariables.RoomIds.Clear();
            StoredVariables.Roomnames.Clear();
            StoredVariables.WallIds.Clear();
            StoredVariables.WallIndex.Clear();
            StoredVariables.FloorBoundary.Clear();
            StoredVariables.RoofBoundary.Clear();
            StoredVariables.WallBoundary.Clear();
            StoredVariables.FloorIndex.Clear();
            StoredVariables.RoofIndex.Clear();

            //Iterate through all rooms
            while (roomIDitr.MoveNext())
            {
                //Grab the room area of each iteration
                ElementId roomid = roomIDitr.Current;
                Room room = doc.GetElement(roomid) as Room;
                double roomarea = room.Area;

                //Only store room data if area is greater than 0
                if (roomarea != 0)
                {
                    StoredVariables.RoomIds.Add(roomid);
                    StoredVariables.Roomnames.Add(room.Name);
                }
            }

            //Set the location line of every room to the centerline of the wall
            SpatialElementBoundaryOptions options = new SpatialElementBoundaryOptions();
            options.SpatialElementBoundaryLocation = SpatialElementBoundaryLocation.Center;

            //Iterate through all rooms
            foreach (ElementId rooms in StoredVariables.RoomIds)
            {
                //Recieve the bounding segments of the room
                Room room = doc.GetElement(rooms) as Room;
                IList<IList<Autodesk.Revit.DB.BoundarySegment>> boundinglines = room.GetBoundarySegments(options);

                //Iterate through all segments of the room
                foreach (IList<Autodesk.Revit.DB.BoundarySegment> b in boundinglines)
                {
                    List<List<String>> Roomsegments = new List<List<string>>();
                    List<string> floorpts = new List<string>();
                    List<string> roofpts = new List<string>();

                    //Iterate through each segment
                    foreach (Autodesk.Revit.DB.BoundarySegment s in b)
                    {
                        //Retrieve the element responsible for creating the curve (Wall)
                        Element currentwall = s.Element;
                        Curve crv = s.Curve;
                        StoredVariables.WallIds.Add(currentwall.Id);
                        String wallname = currentwall.Name;
                        
                        //Index the Wall to associate each line with a walltype
                        int Index = StoredVariables.WallTypes.FindIndex(wall =>wall == wallname);
                        StoredVariables.WallIndex.Add(Index);

                        //Define wall elevations
                        Level bottomLevel = currentwall.Level;
                        double bottomElevation = bottomLevel.Elevation;
                        double topElevation = bottomElevation;

                        //Get height of the wall 
                        Parameter p = currentwall.get_Parameter(BuiltInParameter.WALL_HEIGHT_TYPE);
                        //Set top elevation of wall
                        if (null != p)
                        {
                            ElementId id = p.AsElementId();
                            Level topLevel = currentwall.Document.GetElement(id) as Level;
                            topElevation = topLevel.Elevation;
                        }

                        //Define z translation of bouinding lines
                        double height = topElevation - bottomElevation;
                        XYZ addheight = new XYZ(0,0,height);

                        //Apply translation to start and end points to create the vertices of the wall
                        XYZ stpt = crv.get_EndPoint(0);
                        XYZ endpt = crv.get_EndPoint(1);
                        XYZ stpt2 = stpt + addheight;
                        XYZ endpt2 = endpt + addheight;

                        //Apply the metric conversion consistent with EnergyPlus requirements
                        stpt = stpt*MetricConversion;
                        endpt = endpt * MetricConversion;
                        stpt2 = stpt2 * MetricConversion;
                        endpt2 = endpt2 * MetricConversion;

                        //Add points to a list
                        List<String> wallpts = new List<string>();
                        wallpts.Add(endpt.ToString());
                        wallpts.Add(endpt2.ToString());
                        wallpts.Add(stpt2.ToString());
                        wallpts.Add(stpt.ToString());
                        
                        //Add list to a list
                        Roomsegments.Add(wallpts);

                        //Define bottom points as floorpoints and top points as roof points
                        floorpts.Add(stpt.ToString());
                        floorpts.Add(endpt.ToString());
                        roofpts.Add(stpt2.ToString());
                        roofpts.Add(stpt2.ToString());
                        
                    }

                    List<string> newfloorpts = new List<string>();
                    List<string> newroofpts = new List<string>();

                    //Only add unique pts to the list
                    foreach (string pt in floorpts)
                    {
                        if (true != newfloorpts.Exists(xyz => xyz == pt))
                        {
                            newfloorpts.Add(pt);
                        }
                    }

                    //Only add unique pts to the list
                    foreach (string pt in roofpts)
                    {
                        if (true != newroofpts.Exists(xyz => xyz == pt))
                        {
                            newroofpts.Add(pt);
                        }
                    }

                    //Reverse the floor points to be read counterclockwise in EnergyPlus
                    newfloorpts.Reverse();

                    StoredVariables.FloorBoundary.Add(newfloorpts);
                    StoredVariables.RoofBoundary.Add(newroofpts);
                    StoredVariables.WallBoundary.Add(Roomsegments);
                    
                }

                //Filter all floors used in project and add them to an index
                //Needs refining...(Currently doesn't account for multiple floors in a project)
                FilteredElementCollector floorcollector = new FilteredElementCollector(doc);
                List<Floor> Floors = new List<Floor>();
                floorcollector.OfClass(typeof(Floor));

                foreach (Floor f in floorcollector)
                {
                    String floorname = f.Name;
                    int Index = StoredVariables.FloorTypes.FindIndex(floor => floor == floorname);
                    StoredVariables.FloorIndex.Add(Index);
                }

                //Filter all roofs used in project and add them to an index
                //Needs refining...(Currently doesn't account for multiple floors in a project)
                FilteredElementCollector roofcollector = new FilteredElementCollector(doc);
                List<RoofBase> Roofs = new List<RoofBase>();
                roofcollector.OfClass(typeof(RoofBase));

                foreach (RoofBase r in roofcollector)
                {
                    String roofname = r.Name;
                    int Index = StoredVariables.RoofTypes.FindIndex(roof => roof == roofname);
                    StoredVariables.RoofIndex.Add(Index);
                }

            }
        }      
    }
}
